package token

import (
	"strconv"
	//"fmt"
	"unicode"
)

type Position struct {
	Row int
	Col int
}

const eof = -1

type Token int

const (
	ILLEGAL Token = iota
	EOF
	COMMENT

	IDENT
	NUMBER
	STRING

	operator_beg
	// Operators and delimiters
	ADD // +
	SUB // -
	MUL // *
	QUO // /
	REM // %

	AND     // &
	OR      // |
	XOR     // ^
	SHL     // <<
	SHR     // >>
	AND_NOT // &^

	ADD_ASSIGN // +=
	SUB_ASSIGN // -=
	MUL_ASSIGN // *=
	QUO_ASSIGN // /=
	REM_ASSIGN // %=

	AND_ASSIGN     // &=
	OR_ASSIGN      // |=
	XOR_ASSIGN     // ^=
	SHL_ASSIGN     // <<=
	SHR_ASSIGN     // >>=
	AND_NOT_ASSIGN // &^=

	LAND  // &&
	LOR   // ||
	ARROW // <-
	INC   // ++
	DEC   // --

	EQL    // ==
	LSS    // <
	GTR    // >
	ASSIGN // =
	NOT    // !

	NEQ      // !=
	LEQ      // <=
	GEQ      // >=
	DEFINE   // :=
	ELLIPSIS // ...

	LPAREN // (
	LBRACK // [
	LBRACE // {
	COMMA  // ,
	PERIOD // .

	RPAREN    // )
	RBRACK    // ]
	RBRACE    // }
	SEMICOLON // ;
	COLON     // :
	operator_end

	Tkey_words_beg
	UNSIGNED
	SIGNED
	VOID
	CHAR
	SHORT
	INT
	LONG
	FLOAT
	DOUBLE

	AUTO
	BREAK
	CASE
	CONST
	CONTINUE
	DEFAULT
	DO
	ELSE
	ENUM
	EXTERN
	FOR
	GOTO
	IF
	REGISTER
	SIZEOF
	STATIC
	STRUCT
	SWITCH
	TYPEDEF
	UNION
	VOLATILE
	WHILE

	Tkey_words_end
)

func (tok Token) String() string {
	s := ""
	if 0 <= tok && tok < Token(len(tokens)) {
		s = tokens[tok]
	}
	if s == "" {
		s = "token(" + strconv.Itoa(int(tok)) + ")"
	}
	return s
}

const (
	LowestPrec  = 0
	UnaryPrec   = 6
	HighestPrec = 7
)

func (op Token) Precedence() int {
	switch op {
	case LOR:
		return 1
	case LAND:
		return 2
	case EQL, NEQ, LSS, LEQ, GTR, GEQ:
		return 3
	case ADD, SUB, OR, XOR:
		return 4
	case MUL, QUO, REM, SHL, SHR, AND, AND_NOT:
		return 5
	}
	return LowestPrec
}

var tokens = []string{
	ILLEGAL: "ILLEGAL",
	EOF:     "EOF",
	COMMENT: "COMMENT",
	IDENT:   "IDENT",
	NUMBER:  "NUMBER",
	STRING:  "STRING",

	ADD: "+",
	SUB: "-",
	MUL: "*",
	QUO: "/",
	REM: "%",

	AND:     "&",
	OR:      "|",
	XOR:     "^",
	SHL:     "<<",
	SHR:     ">>",
	AND_NOT: "&^",

	ADD_ASSIGN: "+=",
	SUB_ASSIGN: "-=",
	MUL_ASSIGN: "*=",
	QUO_ASSIGN: "/=",
	REM_ASSIGN: "%=",

	AND_ASSIGN:     "&=",
	OR_ASSIGN:      "|=",
	XOR_ASSIGN:     "^=",
	SHL_ASSIGN:     "<<=",
	SHR_ASSIGN:     ">>=",
	AND_NOT_ASSIGN: "&^=",

	LAND:  "&&",
	LOR:   "||",
	ARROW: "<-",
	INC:   "++",
	DEC:   "--",

	EQL:    "==",
	LSS:    "<",
	GTR:    ">",
	ASSIGN: "=",
	NOT:    "!",

	NEQ:      "!=",
	LEQ:      "<=",
	GEQ:      ">=",
	DEFINE:   ":=",
	ELLIPSIS: "...",

	LPAREN: "(",
	LBRACK: "[",
	LBRACE: "{",
	COMMA:  ",",
	PERIOD: ".",

	RPAREN:    ")",
	RBRACK:    "]",
	RBRACE:    "}",
	SEMICOLON: ";",
	COLON:     ":",
	UNSIGNED:  "unsigned",
	SIGNED:    "signed",
	VOID:      "void",
	CHAR:      "char",
	SHORT:     "short",
	INT:       "int",
	LONG:      "long",
	FLOAT:     "float",
	DOUBLE:    "double",

	AUTO:     "auto",
	BREAK:    "break",
	CASE:     "case",
	CONST:    "const",
	CONTINUE: "continue",
	DEFAULT:  "default",
	DO:       "do",
	ELSE:     "else",
	ENUM:     "enum",
	EXTERN:   "extern",
	FOR:      "for",
	GOTO:     "goto",
	IF:       "if",
	REGISTER: "register",
	SIZEOF:   "sizeof",
	STATIC:   "static",
	STRUCT:   "struct",
	SWITCH:   "switch",
	TYPEDEF:  "typedef",
	UNION:    "union",
	VOLATILE: "volatile",
	WHILE:    "while",
}

var keywords map[string]Token

func init() {
	keywords = make(map[string]Token)
	for k := Tkey_words_beg + 1; k < Tkey_words_end; k++ {
		keywords[tokens[k]] = k
	}
}

func Lookup(ident string) Token {
	if tok, is_keyword := keywords[ident]; is_keyword {
		return tok
	}

	return IDENT
}

func (tok Token) IsOperator() bool {
	return operator_beg < tok && tok < operator_end
}

func (tok Token) IsKeyword() bool {
	return Tkey_words_beg < tok && tok < Tkey_words_end
}

func IsKeyword(name string) bool {
	_, ok := keywords[name]

	return ok
}

func IsIdentifier(name string) bool {
	for i, c := range name {
		if !unicode.IsLetter(c) && c != '_' && (i == 0 || !unicode.IsDigit(c)) {
			return false
		}
	}
	return name != "" && !IsKeyword(name)
}
